package service

import (
	"archive/zip"
	"boy-go/modules/generator/constants"
	"boy-go/modules/generator/model"
	"boy-go/modules/generator/repository"
	"boy-go/modules/generator/utils"
	"boy-go/modules/generator/vo"
	"boy-go/pkg/request"
	"boy-go/pkg/xarray"
	"boy-go/pkg/xerror"
	"boy-go/pkg/xstr"
	"boy-go/pkg/xtime"
	"bytes"
	"encoding/json"
	"errors"
	"github.com/jinzhu/copier"
	"gorm.io/gorm"
	"log"
)

func NewGenTableService() GenTableService {
	return &genTableService{
		GenTableRepo:       repository.NewGenTableRepository(),
		GenTableColumnRepo: repository.NewGenTableColumnRepository(),
	}
}

type genTableService struct {
	GenTableRepo       *repository.GenTableRepository
	GenTableColumnRepo *repository.GenTableColumnRepository
}

// GetById 根据id获取配置信息
func (s *genTableService) GetById(tableId int64) (*vo.GenTableModel, error) {
	m, err := s.GenTableRepo.GetById(tableId)
	if err != nil {
		return nil, err
	}
	rsp := new(vo.GenTableModel)
	if err := copier.Copy(rsp, m); err != nil {
		return nil, err
	}
	if m.Options != nil {
		var options map[string]interface{}
		if err := json.Unmarshal([]byte(*m.Options), &options); err != nil {
			return nil, err
		}
		parentMenuId, have := options["parentMenuId"]
		if have {
			rsp.ParentMenuId = int64(parentMenuId.(float64))
		}
	}
	return rsp, nil
}

// List 获取代码生成业务表列表
func (s *genTableService) List(req *vo.QueryGenTableReq) (*vo.ListGenTableRsp, error) {

	var pageReq = &request.PageRequest{
		PageNum:  req.PageNum,
		PageSize: req.PageSize,
	}

	list, total, err := s.GenTableRepo.List(req, pageReq)

	if err != nil {
		return nil, err
	}

	itemList := make([]*vo.GenTableModel, 0)
	for _, v := range list {
		item := new(vo.GenTableModel)
		if err := copier.Copy(&item, v); err != nil {
			continue
		}
		itemList = append(itemList, item)
	}
	return &vo.ListGenTableRsp{
		List:  itemList,
		Total: total,
	}, nil
}

func (s *genTableService) SelectGenTableAll() []*vo.GenTableModel {
	list := make([]*vo.GenTableModel, 0)
	tables := s.GenTableRepo.SelectGenTableAll()
	tableIds := make([]*int64, 0)
	for _, v := range tables {
		tableId := v.TableId
		tableIds = append(tableIds, &tableId)
	}
	columnMaps := make(map[int64][]*vo.GenTableColumnModel)
	columns, err := s.GenTableColumnRepo.SelectGenTableColumnsByTableIds(tableIds)
	for _, v := range columns {
		item := new(vo.GenTableColumnModel)
		if err := copier.Copy(&item, v); err != nil {
			continue
		}
		columnMaps[v.TableId] = append(columnMaps[v.TableId], item)
	}
	if err != nil {
		return list
	}
	if tables != nil {
		for _, v := range tables {
			item := new(vo.GenTableModel)
			if err := copier.Copy(item, v); err != nil {
				continue
			}
			if cs, ok := columnMaps[v.TableId]; ok {
				item.Columns = cs
			}
			list = append(list, item)
		}
	}
	return list
}

// Add 添加代码生成业务表
func (s *genTableService) Add(req *vo.AddGenTableReq) (*vo.AddGenTableRsp, error) {
	m := new(model.GenTable)
	err := copier.Copy(&m, req)
	if err != nil {
		return nil, err
	}
	if err := s.GenTableRepo.Save(m); err != nil {
		return nil, err
	}
	return &vo.AddGenTableRsp{
		TableId: m.TableId,
	}, nil
}

// Edit 修改代码生成业务表
func (s *genTableService) Edit(req *vo.EditGenTableReq) (*vo.EditGenTableRsp, error) {
	m := new(model.GenTable)
	err := copier.Copy(&m, req)
	if err != nil {
		return nil, err
	}
	p := req.Params
	//转换为json字符串
	optionsByte, err := json.Marshal(p)
	if err == nil {
		options := string(optionsByte)
		m.Options = &options
	}
	if err := s.GenTableRepo.Update(m); err != nil {
		return nil, err
	}
	for _, column := range req.Columns {
		tableColumn := new(model.GenTableColumn)
		err = copier.Copy(&tableColumn, column)
		if err == nil {
			err = s.GenTableColumnRepo.Update(tableColumn)
			if err != nil {
				log.Printf("修改字段失败: %w", err)
			}
		}
	}
	return &vo.EditGenTableRsp{
		TableId: m.TableId,
	}, nil
}

// Del 删除代码生成业务表
func (s *genTableService) Del(tableId int64) error {
	return s.GenTableRepo.Delete(tableId)
}

func (s *genTableService) BatchDel(ids []string) error {
	return s.GenTableRepo.BatchDelete(ids)
}

func (s *genTableService) SelectDbTableList(query *vo.QueryGenTableReq) (*vo.ListGenTableRsp, error) {
	var pageReq = &request.PageRequest{
		PageNum:  query.PageNum,
		PageSize: query.PageSize,
	}
	list, total, err := s.GenTableRepo.SelectDbTableList(query, pageReq)
	if err != nil {
		return nil, err
	}
	itemList := make([]*vo.GenTableModel, 0)
	for _, v := range list {
		itemList = append(itemList, &vo.GenTableModel{
			MTableName:    v.MTableName,
			MTableComment: v.MTableComment,
			CreateTime:    xtime.TS.Format(v.CreateAt),
			UpdateTime:    xtime.TS.Format(v.UpdateAt),
		})
	}
	return &vo.ListGenTableRsp{
		List:  itemList,
		Total: total,
	}, nil
}
func (s *genTableService) SelectDbTableListByNames(tables []string) ([]*model.GenTable, error) {
	return s.GenTableRepo.SelectDbTableListByNames(tables)
}

func (s *genTableService) ImportGenTable(tableList []*model.GenTable, operName string) error {

	for _, table := range tableList {
		tableName := table.MTableName
		utils.InitTable(table, operName)
		err := s.GenTableRepo.Save(table)
		if err != nil {
			return err
		}
		genTableColumns, err := s.GenTableColumnRepo.SelectDbTableColumnsByName(*tableName)
		if err != nil {
			return err
		}
		for _, column := range genTableColumns {
			column.TableId = table.TableId
			utils.InitColumnField(column, table)
			err = s.GenTableColumnRepo.Save(column)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// SynchDb 同步数据库
func (s *genTableService) SynchDb(tableName string) error {
	table, err := s.GenTableRepo.GetByTableName(tableName)
	if err != nil {
		return err
	}
	tableColumns, err := s.GenTableColumnRepo.SelectGenTableColumnsByTableId(table.TableId)
	if err != nil {
		return err
	}
	tableColumnMap := make(map[string]*model.GenTableColumn)
	for _, v := range tableColumns {
		tableColumnMap[v.ColumnName] = v
	}
	dbTableColumns, err := s.GenTableColumnRepo.SelectDbTableColumnsByName(tableName)
	if err != nil {
		return err
	}
	if len(dbTableColumns) == 0 {
		return xerror.NewArgument("同步数据失败，原表结构不存在")
	}
	dbTableColumnNames := map[string]string{}
	for _, v := range dbTableColumns {
		dbTableColumnNames[v.ColumnName] = v.ColumnName
	}
	err = s.GenTableRepo.DB.Transaction(func(tx *gorm.DB) error {
		for _, column := range dbTableColumns {
			utils.InitColumnField(column, table)
			if prevColumn, ok := tableColumnMap[column.ColumnName]; ok {
				column.ColumnId = prevColumn.ColumnId
				if column.IsList == "1" {
					// 如果是列表，继续保留查询方式/字典类型选项
					column.DictType = prevColumn.DictType
					column.QueryType = prevColumn.QueryType
				}
				if xstr.IsNotEmpty(&prevColumn.IsRequired) && column.IsPk == "1" && (column.IsInsert == "1" || column.IsEdit == "1") && (column.IsSuperColumn() || column.IsSuperColumn()) {
					// 如果是(新增/修改&非主键/非忽略及父属性)，继续保留必填/显示类型选项
					column.IsRequired = prevColumn.IsRequired
					column.HtmlType = prevColumn.HtmlType
				}
				dbErr := tx.Model(&model.GenTableColumn{}).Where("column_id = ?", column.ColumnId).Updates(column).Omit("column_id").Error
				if dbErr != nil {
					return dbErr
				}
			} else {
				dbErr := tx.Model(&model.GenTableColumn{}).Create(column).Error
				if dbErr != nil {
					return dbErr
				}
			}
		}
		delColumns := make([]int64, 0)
		for _, tableColumn := range tableColumns {
			if _, ok := dbTableColumnNames[tableColumn.ColumnName]; ok == false {
				delColumns = append(delColumns, tableColumn.ColumnId)
			}
		}
		if len(delColumns) > 0 {
			dbErr := tx.Model(&model.GenTableColumn{}).Where("column_id in ?", delColumns).Delete(&model.GenTableColumn{}).Error
			if dbErr != nil {
				return dbErr
			}
		}
		return nil
	})
	return nil
}
func (s *genTableService) PreviewCode(tableId int64) ([]*vo.TemplatePreviewCode, error) {
	codes := make([]*vo.TemplatePreviewCode, 0)

	table, err := s.GenTableRepo.GetById(tableId)
	if err != nil {
		return nil, err
	}
	tableColumns, err := s.GenTableColumnRepo.SelectGenTableColumnsByTableId(tableId)
	if err != nil {
		return nil, err
	}
	tmpls := []map[string]string{
		{"mode": "go", "class": "model"},
		{"mode": "go", "class": "vo"},
		{"mode": "go", "class": "repository"},
		{"mode": "go", "class": "service"},
		{"mode": "go", "class": "serviceimpl"},
		{"mode": "go", "class": "handler"},
		{"mode": "js", "class": "api"},
		{"mode": "vue", "class": "index"},
		{"mode": "sql", "class": "menu"},
	}
	subTable := &model.GenTable{}
	subTableColumns := make([]*model.GenTableColumn, 0)
	if *table.TplCategory == constants.TPL_SUB {
		tmpls = xarray.Insert(tmpls, 1, map[string]string{"mode": "go", "class": "sub-model"})
		tmpls = xarray.Insert(tmpls, 3, map[string]string{"mode": "go", "class": "sub-vo"})
		subTable, err = s.GenTableRepo.GetByTableName(*table.SubTableName)
		if err != nil {
			return nil, err
		}
		subTableColumns, err = s.GenTableColumnRepo.SelectGenTableColumnsByTableId(subTable.TableId)
		if err != nil {
			return nil, err
		}
	}
	for _, tmpl := range tmpls {
		mode, _ := tmpl["mode"]
		class, _ := tmpl["class"]
		codes = append(codes, &vo.TemplatePreviewCode{
			FileName: class + "." + mode,
			Value:    utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, mode, class),
		})
	}
	return codes, nil
}

// ValidateEdit 修改保存参数校验
func (s *genTableService) ValidateEdit(req *vo.EditGenTableReq) error {
	if req.TplCategory == constants.TPL_TREE {
		treeCode, have := req.Params[constants.TREE_CODE]
		if !have || treeCode == "" {
			return errors.New("树编码字段不能为空")
		}
		treeParentCode, have := req.Params[constants.TREE_PARENT_CODE]
		if !have || treeParentCode == "" {
			return errors.New("树父编码字段不能为空")
		}
		treeName, have := req.Params[constants.TREE_NAME]
		if !have || treeName == "" {
			return errors.New("树名称字段不能为空")
		}
	}
	if req.TplCategory == constants.TPL_SUB {
		if req.SubTableName == nil || *req.SubTableName == "" {
			return errors.New("关联子表的表名不能为空")
		}
		if req.SubTableFkName == nil || *req.SubTableFkName == "" {
			return errors.New("子表关联的外键名不能为空")
		}
	}
	return nil
}
func (s *genTableService) DownloadCode(tableNames []string) ([]byte, error) {
	// 创建一个缓冲区用于保存 zip 数据
	buf := new(bytes.Buffer)
	// 创建一个新的 zip writer
	zw := zip.NewWriter(buf)
	for _, tableName := range tableNames {
		table, err := s.GenTableRepo.GetByTableName(tableName)
		if err != nil {
			log.Printf("获取表信息失败: %w", err)
		}
		tableColumns, err := s.GenTableColumnRepo.SelectGenTableColumnsByTableId(table.TableId)
		if err != nil {
			log.Printf("获取表字段信息失败: %w", err)
		}
		subTable := &model.GenTable{}
		subTableColumns := make([]*model.GenTableColumn, 0)

		if *table.TplCategory == constants.TPL_SUB {
			subTable, err = s.GenTableRepo.GetByTableName(*table.SubTableName)
			if err != nil {
				return nil, err
			}
			subTableColumns, err = s.GenTableColumnRepo.SelectGenTableColumnsByTableId(subTable.TableId)
			if err != nil {
				return nil, err
			}
		}

		packageName := table.PackageName
		businessName := table.BusinessName
		vueName := "index"
		modelStruct := utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, "go", "model")
		voStruct := utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, "go", "vo")
		repositoryStruct := utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, "go", "repository")
		serviceStruct := utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, "go", "service")
		serviceImplStruct := utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, "go", "serviceimpl")
		handlerStruct := utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, "go", "handler")

		apiJs := utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, "js", "api")
		indexVue := utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, "vue", "index")
		// 转为文件并下载
		files := map[string]string{
			"go/" + *packageName + "/model/" + tableName + "_model.go":                 modelStruct,
			"go/" + *packageName + "/vo/" + tableName + "_vo.go":                       voStruct,
			"go/" + *packageName + "/repository/" + tableName + "_repository.go":       repositoryStruct,
			"go/" + *packageName + "/service/" + tableName + "_service.go":             serviceStruct,
			"go/" + *packageName + "/service/" + tableName + "_serviceimpl.go":         serviceImplStruct,
			"go/" + *packageName + "/handler/" + tableName + "_handler.go":             handlerStruct,
			"vue/api/" + *packageName + "/" + *businessName + ".js":                    apiJs,
			"vue/views/" + *packageName + "/" + *businessName + "/" + vueName + ".vue": indexVue,
		}
		if *table.TplCategory == constants.TPL_SUB {
			subModelStruct := utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, "go", "sub-model")
			subVoStruct := utils.GeneratorByType(*table, tableColumns, subTable, subTableColumns, "go", "sub-vo")
			files["go/"+*packageName+"/model/"+*table.SubTableName+"_model.go"] = subModelStruct
			files["go/"+*packageName+"/vo/"+*table.SubTableName+"_vo.go"] = subVoStruct
		}

		// 遍历每个字符串，创建 zip 中的文件
		for filename, content := range files {
			// 添加一个文件到 zip
			f, err := zw.Create(filename)
			if err != nil {
				return nil, err
			}
			// 写入文件内容
			_, err = f.Write([]byte(content))
			if err != nil {
				return nil, err
			}
		}
		// 关闭 zip writer
		err = zw.Close()
		if err != nil {
			return nil, err
		}
	}
	return buf.Bytes(), nil
}
